//tcp_server.hpp
#pragma once
#include <iostream>
#include <sys/socket.h>
#include <sys/types.h>
#include <string>
#include <cerrno>
#include <cstring>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "Err.hpp"
#include <unistd.h>
#include <functional>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>
#include "ThreadPool.hpp"
#include "Log.hpp"

using namespace std;
using func_t = function<string(string)>;//和typedef一样的效果

//version-3  线程版本时需要传递能够使用的参数使用的
/*class Server;
class PthreadData
{
public:
    PthreadData(uint16_t p, string &i, Server *c, int ck)
        : port(p), ip(i), current(c), sock(ck)
    {
    }
    uint16_t port;
    string ip;
    Server *current;
    int sock;
};*/


class Server
{
    const static uint16_t defaultport = 8081;
    const static int backlog = 32;

public:
    Server(func_t func, uint16_t port = defaultport)
        : _port(port), _func(func)
    {
    }
    void Init()
    {
        //version-1 多进程信号版本时需要，子进程运行完自己退出，不需要父进程接收
        //signal(SIGCHLD,SIG_IGN);

        //  1.创建socket文件
        _listensock = socket(AF_INET, SOCK_STREAM, 0);
        if (_listensock < 0)
        {
            // cerr << "socket error" << endl;
            LogMessage(4, "socket error");
            exit(SOCKET_ERR);
        }

        // 2.使_listensock和服务器的IP和port绑定在一起
        struct sockaddr_in local;
        //清零
        bzero(&local, sizeof(local));
        //IP，云服务器中，IP让服务器自己指定
        local.sin_addr.s_addr = INADDR_ANY;
        //表示IPv4协议
        local.sin_family = AF_INET;
        //将端口转换成网络序列
        local.sin_port = htons(_port);
        socklen_t len = sizeof(local);
        if (bind(_listensock, (struct sockaddr *)&local, len) < 0)
        {
            // cerr << "bind error" << endl;
            LogMessage(4, "bind error");
            exit(BIND_ERR);
        }
        
        // 3.开始监听
        //backlog表示请求连接队列的最大长度，用于限制排队请求的个数，目前允许的最大值为5。如果没有错误发生，listen()返回0
        int ret = listen(_listensock, backlog);
        if (ret < 0)
        {
            // cerr << "listen error" << endl;
            LogMessage(4, "listen error");
            exit(LISTEN_ERR);
        }
    }

    // void Service(uint16_t clientport, const string ip);
    void Start()
    {
        while (1)
        {
            struct sockaddr_in client;
            socklen_t len = sizeof(client);
            // 4.获取连接
            int sock = accept(_listensock, (struct sockaddr *)&client, &len);
            if (sock < 0)
            {
                // cerr << "accept error" << endl;
                LogMessage(2, "accept error");
                continue;
            }

            //5.获取链接成功，开始业务处理
            uint16_t clientport = ntohs(client.sin_port);
            string clientip = inet_ntoa(client.sin_addr);
            // cout << "获取链接成功：" << sock << " from " << _listensock << "," << ip << "-" << clientport << endl;
            LogMessage(Information, "获取链接成功:%d from %d,%s-%d", sock, _listensock, clientip.c_str(), clientport);

            // Service(clientport, ip);

            // version-1 多进程信号版本
            /*int pid=fork();
            if(pid==0)
            {
                //子进程
                close(_listensock);//子进程关闭不需要的描述符
                Service(clientport,ip,sock);
                exit(0);
            }
            else if(pid<0)
            {
                close(sock);
                continue;
            }
            close(sock);//父进程关闭可以通信sock描述符，否则在不停地accept过程中，sock会越来越多
            */

            // version-2 多进程等待版本
            //通过简单的父子进程需要等待，即使是使用轮询，也会使代码比较复杂
            /*int pid=fork();//父进程的fd，会被child继承，父子不用同一张文件描述符表，但是子进程会拷贝父进程的文件描述符表
            if(pid==0)
            {
                //子进程
                if(0<fork())
                {
                    
                    //child退了，孙子进程运行，因此孙子进程变成孤儿进程
                    exit(0);
                }

                孙子进程关闭不需要的_listensock
                close(_listensock);
                Service(clientport,ip,sock);
                exit(0);
            }
            else if(pid<0)
            {
                close(sock);//父进程一定要关闭不需要的fd，不关闭，会导致fd泄漏
                continue;
            }
            close(sock);//关闭不需要的描述符
            waitpid(pid,nullptr,0);//这里等待子进程退出时很快的事情了
            */


            // version-3  线程版本
            /*pthread_t t1;
            PthreadData* ptr=new PthreadData(clientport,ip,this,sock);
            pthread_create(&t1,nullptr,routine,ptr);*/

            //version-4 线程池方案
            ThreadPool<Task> *pool = ThreadPool<Task>::GetInstance();
            //push我们的Task就开始运行Service了
            pool->push(Task(clientport, clientip, bind(&Server::Service, this, placeholders::_1, placeholders::_2, placeholders::_3), sock));
        }
    }

    // version-3  线程版本时使用
    /*static void *routine(void *arg)
    {
        pthread_detach(pthread_self());
        PthreadData *data = static_cast<PthreadData *>(arg);
        data->current->Service(data->port, data->ip, data->sock);
    }
    */


   //上面的Start的工作是和客户端进行连接，Service的工作是和客户端通信
    void Service(uint16_t clientport, const string clientip, int sock)
    {
        while (1)
        {
            char buff[1024];
            //读取客户端发来的信息
            ssize_t n = read(sock, buff, sizeof(buff) - 1);
            if (n > 0)
            {
                buff[n] = 0;

                // cout<<ip<<"-"<<clientport<<":"<<buff<<endl;
                LogMessage(Information, "%s-%d:%s", clientip.c_str(), clientport, buff);

                //进行业务处理
                string str = _func(buff);
                //向客户端发送处理完的结果
                write(sock, str.c_str(), str.size());
            }
            else if (n == 0)
            {
                // 对方把链接关闭了
                // cerr << "client quit,me too" << endl;
                LogMessage(Information, "client-%s-%d quit,me too",clientip.c_str(),clientport);
                break;
            }
            else
            {
                //读取错误
                // cerr << "read error" << strerror(errno) << endl;
                LogMessage(errno, "read error,%s", strerror(errno));
                break;
            }
        }   
        //同一进程的线程共用文件描述符
        close(sock);
    }

private:
    uint16_t _port;//服务器端口
    int _listensock;//socket的返回值，监听时使用
    func_t _func;//业务处理接口
};
